﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Test
{
    /// <summary>
    /// 八字算法
    /// </summary>
    public class BaziAlgorithm
    {


        public static char[] TianGanArray { get; set; }

        public static char[] DiZhiArray { get; set; }

        /// <summary>
        /// 月份的八字速查表
        /// </summary>
        public static Dictionary<string, string[]> monthTianGanDiZhi = new Dictionary<string, string[]>();

        /// <summary>
        /// 时辰的八字速查表
        /// </summary>
        public static Dictionary<string, string[]> hourTianGanDiZhi = new Dictionary<string, string[]>();

        static BaziAlgorithm()
        {
            TianGanArray = "癸甲乙丙丁戊己庚辛壬".ToCharArray();

            DiZhiArray = "亥子丑寅卯辰巳午未申酉戌".ToCharArray();

            monthTianGanDiZhi.Add("甲己", new string[]
            {
                 "丁丑","丙寅","丁卯","戊辰","己巳","庚午","辛未","壬申","癸酉","甲戊","乙亥","丙子"
            });

            monthTianGanDiZhi.Add("乙庚", new string[]{
                 "己丑","戊寅","己卯","庚辰","辛巳","壬午","癸未","甲申","乙酉","丙戌","丁亥","戊子"
            });

            monthTianGanDiZhi.Add("丙辛", new string[]{
                 "辛丑","庚寅","辛卯","壬辰","癸巳","甲午","乙未","丙申","丁酉","戊戌","己亥","庚子"
            });

            monthTianGanDiZhi.Add("丁壬", new string[]{
                 "癸丑","壬寅","癸卯","甲辰","乙巳","丙午","丁未","戊申","己酉","庚戌","辛亥","壬子"
            });

            monthTianGanDiZhi.Add("戊癸", new string[]{
                 "乙丑","甲寅","乙卯","丙辰","丁巳","戊午","己未","庚申","辛酉","壬戌","癸亥","甲子"
            });



            hourTianGanDiZhi.Add("甲己", new string[] {
                "甲子", "乙丑", "丙寅", "丁卯", "戊辰", "己巳", "庚午", "辛未", "壬申", "癸酉", "甲戊", "乙亥",
            });

            hourTianGanDiZhi.Add("乙庚", new string[] {
                "丙子", "丁丑", "戊寅", "己卯", "庚辰", "辛巳", "壬午", "癸未", "甲申", "乙酉", "丙戌", "丁亥"
            });

            hourTianGanDiZhi.Add("丙辛", new string[] {
                "戊子", "己丑", "庚寅", "辛卯", "壬辰", "癸巳", "甲午", "乙未", "丙申", "丁酉", "戊戌", "己亥"
            });

            hourTianGanDiZhi.Add("丁壬", new string[] {
                "庚子", "辛丑", "壬寅", "癸卯", "甲辰", "乙巳", "丙午", "丁未", "戊申", "己酉", "庚戌", "辛亥"
            });

            hourTianGanDiZhi.Add("戊癸", new string[] {
                "壬子", "癸丑", "甲寅", "乙卯", "丙辰", "丁巳", "戊午", "己未", "庚申", "辛酉", "壬戌", "癸亥"
            });
        }

        /// <summary>
        /// 根据公历生日获取八字
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public string GetBaZi(DateTimeOffset dt)
        {
            if (dt.Hour >= 23)
            {
                dt = dt.AddHours(1);
            }
            var year = dt.Year;
            var month = dt.Month;
            if (month == 1 || month == 2)
            {
                --year;
                month += 12;
            }

            var tianGanIndex = (year - 3) % 10;
            var diZhiIndex = (year - 3) % 12;
            var baziYear = $"{TianGanArray[tianGanIndex]}{DiZhiArray[diZhiIndex]}";


            var mongthKey = monthTianGanDiZhi.Keys.Where(x => x.Contains(TianGanArray[tianGanIndex])).FirstOrDefault();
            var baziMonth = monthTianGanDiZhi[mongthKey][month - 1];

            #region 获取日柱八字
            var baziDay = string.Empty;
            var c = year / 100;  // 公历年份的前两位
            var y = year % 100;  // 公历年份的后两位
            var g = 4 * c + c / 4 + 5 * y + y / 4 + (month + 1) * 3 / 5 + dt.Day - 3;
            var mgIndex = g % 10;
            baziDay += TianGanArray[mgIndex];
            var i = 0;
            if (month % 2 == 0)
            {
                i = 6;
            }
            var mdIndex = (4 * c + g + 10 + i) % 12;
            baziDay += DiZhiArray[mdIndex];
            #endregion

            #region 获取时辰八字
            var hourArrayKey = hourTianGanDiZhi.Keys.Where(x => x.Contains(TianGanArray[mgIndex])).FirstOrDefault();
            var hourArray = hourTianGanDiZhi[hourArrayKey];
            var hour = dt.Hour;
            if (hour % 2 > 0)
            {
                ++hour;
            }
            var hourIndex = hour / 2;
            if (hourIndex >= 12)
            {
                hourIndex = 0;
            }
            var hourBazi = hourArray[hourIndex];
            #endregion

            return $"{baziYear}-{baziMonth}-{baziDay}-{hourBazi}";
        }



        double[][] TianGan_Strength = new double[12][]
        {
        new double[] {1.2, 1.2,  1.0,  1.0,  1.0,  1.0,  1.0,  1.0,  1.2,  1.2},

         new double[]{1.06,  1.06, 1.0,  1.0,  1.1,  1.1,  1.14, 1.14,  1.1,  1.1},


        new double[] {1.14,   1.14,  1.2,  1.2,  1.06,  1.06,  1.0,  1.0,  1.0,  1.0},


        new double[] {1.2, 1.2,  1.2,  1.2,  1.0,  1.0,  1.0,  1.0,  1.0,  1.0},


        new double[] {1.1, 1.1,  1.06,  1.06,   1.1,  1.1,  1.1,  1.1,  1.04, 1.04},


        new double[] {1.0, 1.0,  1.14,   1.14,   1.14,  1.14, 1.06,   1.06, 1.06,  1.06},


        new double[] {1.0, 1.0,  1.2,  1.2,  1.2,  1.2,  1.0,  1.0,  1.0,  1.0},


        new double[] {1.04,  1.04,  1.1,  1.1,  1.16,  1.16,  1.1,  1.1,  1.0,  1.0},


        new double[] {1.06,  1.06,   1.0,  1.0,  1.0,  1.0,  1.14,  1.14,    1.2,  1.2},


        new double[] {1.0, 1.0,  1.0,  1.0,  1.0,  1.0,  1.2,  1.2,  1.2,  1.2},


        new double[] {1.0, 1.0,  1.04,   1.04,    1.14,  1.14,   1.16, 1.16,  1.06,   1.06},


        new double[] {1.2, 1.2,  1.0,  1.0,  1.0,  1.0,  1.0,  1.0,  1.14, 1.14}
        };



        struct ZISTRENGTH
        {


            public char diZhi;


            public char zhiCang;


            public double[] strength;


            public ZISTRENGTH(char a, char b, double[] c)
            {
                diZhi = a; zhiCang = b; strength = c;
            }
        };




        ZISTRENGTH[] DiZhi_Strength = new ZISTRENGTH[]


        {


        new ZISTRENGTH ('子', '癸', new double[] {1.2,1.1, 1.0, 1.0,        1.04, 1.06, 1.0,1.0, 1.2, 1.2, 1.06, 1.14}),


         new ZISTRENGTH('丑', '癸', new double[] {0.36,0.33, 0.3, 0.3, 0.312, 0.318, 0.3, 0.3, 0.36, 0.36, 0.318, 0.342}),


         new ZISTRENGTH('丑', '辛', new double[] {0.2,0.228, 0.2, 0.2, 0.23, 0.212, 0.2,         0.22,0.228, 0.248, 0.232, 0.2}),


         new ZISTRENGTH('丑', '己',new double[]  {0.5,0.55, 0.53, 0.5, 0.55, 0.57, 0.6, 0.58, 0.5, 0.5, 0.57, 0.5}),


         new ZISTRENGTH('寅', '丙', new double[] {0.3,0.3, 0.36, 0.36, 0.318, 0.342, 0.36, 0.33, 0.3, 0.3, 0.342, 0.318}),


         new ZISTRENGTH('寅', '甲',new double[]  {0.84,0.742, 0.798, 0.84, 0.77, 0.7, 0.7, 0.728, 0.742, 0.7, 0.7, 0.84}),


         new ZISTRENGTH('卯', '乙', new double[] {1.2,1.06, 1.14, 1.2, 1.1, 1.0, 1.0, 1.04, 1.06, 1.0, 1.0, 1.2}),


         new ZISTRENGTH('辰', '乙',new double[]  {0.36,0.318, 0.342, 0.36, 0.33, 0.3, 0.3, 0.312, 0.318, 0.3, 0.3, 0.36}),


         new ZISTRENGTH('辰', '癸', new double[] {0.24,0.22,  0.2, 0.2, 0.208, 0.2, 0.2, 0.2,0.24, 0.24, 0.212, 0.228}),


         new ZISTRENGTH('辰', '戊',new double[]  {0.5,0.55, 0.53, 0.5, 0.55, 0.6, 0.6, 0.58, 0.5, 0.5, 0.57, 0.5}),


        new ZISTRENGTH('巳', '庚', new double[] {0.3,0.342, 0.3, 0.3, 0.33, 0.3, 0.3, 0.33, 0.342, 0.36, 0.348, 0.3}),


         new ZISTRENGTH('巳', '丙', new double[] {0.7,0.7, 0.84, 0.84, 0.742, 0.84, 0.84, 0.798, 0.7, 0.7, 0.728, 0.742}),


         new ZISTRENGTH('午', '丁', new double[] {1.0,1.0, 1.2, 1.2, 1.06, 1.14, 1.2, 1.1, 1.0, 1.0, 1.04, 1.06}),


         new ZISTRENGTH('未', '丁', new double[] {0.3,0.3, 0.36, 0.36, 0.318, 0.342, 0.36, 0.33, 0.3, 0.3, 0.312, 0.318}),


         new ZISTRENGTH('未', '乙',new double[]  {0.24,0.212, 0.228, 0.24, 0.22, 0.2, 0.2, 0.208, 0.212, 0.2, 0.2, 0.24}),


        new ZISTRENGTH('未', '己', new double[] {0.5,0.55, 0.53, 0.5, 0.55, 0.57, 0.6, 0.58, 0.5, 0.5, 0.57, 0.5}),


         new ZISTRENGTH('申', '壬', new double[] {0.36,0.33, 0.3, 0.3, 0.312, 0.318, 0.3, 0.3, 0.36, 0.36, 0.318, 0.342}),


         new ZISTRENGTH('申', '庚',new double[]  {0.7,0.798, 0.7, 0.7, 0.77, 0.742, 0.7, 0.77, 0.798, 0.84, 0.812, 0.7}),


         new ZISTRENGTH('酉', '辛',new double[]  {1.0,1.14, 1.0, 1.0, 1.1, 1.06, 1.0, 1.1, 1.14, 1.2, 1.16, 1.0}),


         new ZISTRENGTH('戌', '辛',new double[]  {0.3,0.342, 0.3, 0.3, 0.33, 0.318, 0.3, 0.33, 0.342, 0.36, 0.348, 0.3}),


         new ZISTRENGTH('戌', '丁', new double[] {0.2,0.2, 0.24, 0.24, 0.212, 0.228, 0.24, 0.22, 0.2, 0.2, 0.208, 0.212}),
         new ZISTRENGTH('戌', '戊',new double[]  {0.5,0.55, 0.53, 0.5, 0.55, 0.57, 0.6, 0.58, 0.5, 0.5, 0.57, 0.5}),
         new ZISTRENGTH('亥', '甲',new double[]  {0.36,0.318, 0.342, 0.36, 0.33, 0.3, 0.3, 0.312, 0.318, 0.3, 0.3, 0.36}),
         new ZISTRENGTH('亥', '壬',new double[]  {0.84,0.77, 0.7, 0.7, 0.728, 0.742, 0.7, 0.7, 0.84, 0.84, 0.724, 0.798})
        };

        char[] WuXingTable = new char[] { '金', '木', '水', '火', '土' };


        int[] TianGan_WuXingProp = new int[10] { 1, 1, 3, 3, 4, 4, 0, 0, 2, 2 };
        int[] DiZhi_WuXingProp = new int[12] { 2, 4, 1, 1, 4, 3, 3, 4, 0, 0, 4, 2 };
        int[] GenerationSourceTable = new int[5] { 4, 2, 0, 1, 3 };


        public const string tian_gan = "甲乙丙丁戊己庚辛壬癸";

        public const string di_zhi = "子丑寅卯辰巳午未申酉戌亥";

        int ComputeGanIndex(char gan)
        {
            int i;
            for (i = 0; i < 10; i++)
                if (tian_gan[i] == gan) break;

            if (i >= 10) return -1;

            return i;
        }

        int ComputeZhiIndex(char zhi)
        {
            int i;

            for (i = 0; i < 12; i++)
                if (di_zhi[i] == zhi) break;


            if (i >= 12) return -1;

            return i;
        }

        static string sResultBuf;   // 用作八字测算结果返回的字符缓冲区


        // 根据八字计算五行平衡
        // 输入参数：bazi，年月日时的干支，即俗称的八字
        // 输出结果：分析结果字符串，Unicode编码
        public string EvalBazi(string bazi)
        {
            double[] strengthResult = new double[5];


            int monthIndex = ComputeZhiIndex(bazi[3]);


            if (monthIndex == -1) return "";

            sResultBuf = bazi;
            sResultBuf += "\n\n五行强度：天干 + 支藏\n";


            for (int wuXing = 0; wuXing < 5; wuXing++)
            {
                double value1 = 0.0, value2 = 0.0;
                int i;


                //扫描4个天干
                for (i = 0; i < 8; i += 2)
                {
                    char gan = bazi[i];


                    int index = ComputeGanIndex(gan);


                    if (index == -1) return "";

                    if (TianGan_WuXingProp[index] == wuXing)
                        value1 += TianGan_Strength[monthIndex][index];
                }

                //扫描支藏
                for (i = 1; i < 8; i += 2)
                {
                    char zhi = bazi[i];


                    for (int j = 0; j < DiZhi_Strength.Length; j++)
                    {


                        if (DiZhi_Strength[j].diZhi == zhi)
                        {


                            int zhiCangIndex = ComputeGanIndex(DiZhi_Strength[j].zhiCang);


                            if (zhiCangIndex == -1) return "";


                            if (TianGan_WuXingProp[zhiCangIndex] == wuXing)
                            {
                                value2 += DiZhi_Strength[j].strength[monthIndex];
                                break;
                            }
                        }
                    }
                }


                strengthResult[wuXing] = value1 + value2;

                //输出一行计算结果
                {
                    string preStr;
                    string tmpBuf;


                    tmpBuf = value1.ToString("0.00") + " + " + value2.ToString("0.00") + " = " + (value1 + value2).ToString("0.00") + "\n";

                    preStr = WuXingTable[wuXing] + ":\t";
                    sResultBuf += preStr;
                    sResultBuf += tmpBuf;
                }
            }


            //根据日干求命里属性
            int fateProp, srcProp;
            {
                string tmpWBuf;


                fateProp = TianGan_WuXingProp[ComputeGanIndex(bazi[4])];


                if (fateProp == -1) return "";

                tmpWBuf = "\n命属" + WuXingTable[fateProp] + "\n\n";
                sResultBuf += tmpWBuf;
            }


            //求同类和异类的强度值
            srcProp = GenerationSourceTable[fateProp];
            {
                string preStr;
                string tmpBuf;
                double tongLei = strengthResult[fateProp] + strengthResult[srcProp];
                double yiLei = 0.0;

                for (int i = 0; i < 5; i++) yiLei += strengthResult[i];


                yiLei -= tongLei;

                tmpBuf = strengthResult[fateProp].ToString("0.00") + " + " + strengthResult[srcProp].ToString("0.00") + " = " + tongLei.ToString("0.00") + "\n";
                preStr = "同类：" + WuXingTable[fateProp] + "+" + WuXingTable[srcProp] + "，";


                sResultBuf += preStr;
                sResultBuf += tmpBuf;
                tmpBuf = yiLei.ToString("0.00") + "\n";
                sResultBuf += "异类：总和为 " + tmpBuf;
            }
            return sResultBuf;
        }
    }
}
